import json
from channels.generic.websocket import AsyncWebsocketConsumer
from channels.db import database_sync_to_async
from django.contrib.auth import get_user_model
from .models import Conversation, Message, Group, GroupMessage, GroupMembership
from .redis_utils import chat_redis

User = get_user_model()


class ChatConsumer(AsyncWebsocketConsumer):
    """聊天WebSocket消费者"""
    
    async def connect(self):
        self.conversation_id = self.scope['url_route']['kwargs']['conversation_id']
        self.conversation_group_name = f'chat_{self.conversation_id}'
        
        # 检查用户是否参与此会话
        if await self.check_conversation_access():
            # 加入会话组
            await self.channel_layer.group_add(
                self.conversation_group_name,
                self.channel_name
            )
            
            await self.accept()
        else:
            await self.close()
    
    async def disconnect(self, close_code):
        # 离开会话组
        await self.channel_layer.group_discard(
            self.conversation_group_name,
            self.channel_name
        )
    
    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message_type = text_data_json.get('type')
        
        if message_type == 'chat_message':
            await self.handle_chat_message(text_data_json)
        elif message_type == 'typing':
            await self.handle_typing(text_data_json)
        elif message_type == 'read_messages':
            await self.handle_read_messages(text_data_json)
    
    async def handle_chat_message(self, data):
        """处理聊天消息"""
        content = data['message']
        sender = self.scope['user']
        
        # 保存消息到数据库
        message = await self.save_message(sender, content)
        
        # 保存消息到Redis
        message_data = {
            'id': message.id,
            'content': message.content,
            'sender_id': sender.id,
            'sender_username': sender.username,
            'sender_nickname': sender.nickname or sender.username,
            'sender_avatar': sender.avatar.url if sender.avatar else None,
            'created_at': message.created_at.isoformat(),
            'message_type': message.message_type,
            'is_read': False,
        }
        await self.save_message_to_redis(message_data)
        
        # 发送消息到会话组（包括发送者和接收者）
        await self.channel_layer.group_send(
            self.conversation_group_name,
            {
                'type': 'chat_message',
                'message': {
                    'id': message.id,
                    'content': message.content,
                    'sender_id': sender.id,
                    'sender': {
                        'id': sender.id,
                        'username': sender.username,
                        'nickname': sender.nickname or sender.username,
                        'avatar': sender.avatar.url if sender.avatar else None,
                    },
                    'created_at': message.created_at.isoformat(),
                    'message_type': message.message_type,
                }
            }
        )
        
        # 发送通知给对方用户（用于更新未读计数和显示通知）
        other_user = await self.get_other_participant()
        if other_user:
            await self.channel_layer.group_send(
                f'notifications_{other_user.id}',
                {
                    'type': 'send_message_notification',
                    'message': {
                        'id': message.id,
                        'content': message.content,
                        'sender': {
                            'id': sender.id,
                            'username': sender.username,
                            'nickname': sender.nickname or sender.username,
                            'avatar': sender.avatar.url if sender.avatar else None,
                        },
                        'conversation_id': self.conversation_id,
                        'created_at': message.created_at.isoformat(),
                    }
                }
            )
    
    async def handle_typing(self, data):
        """处理正在输入状态"""
        await self.channel_layer.group_send(
            self.conversation_group_name,
            {
                'type': 'typing',
                'user': {
                    'id': self.scope['user'].id,
                    'username': self.scope['user'].username,
                    'nickname': self.scope['user'].nickname or self.scope['user'].username,
                },
                'is_typing': data.get('is_typing', False)
            }
        )
    
    async def handle_read_messages(self, data):
        """处理消息已读状态"""
        message_ids = data.get('message_ids', [])
        await self.mark_messages_as_read(message_ids)
        
        await self.channel_layer.group_send(
            self.conversation_group_name,
            {
                'type': 'messages_read',
                'message_ids': message_ids,
                'user_id': self.scope['user'].id
            }
        )
    
    async def chat_message(self, event):
        """发送聊天消息到WebSocket"""
        await self.send(text_data=json.dumps({
            'type': 'chat_message',
            'message': event['message']
        }))
    
    async def typing(self, event):
        """发送正在输入状态到WebSocket"""
        await self.send(text_data=json.dumps({
            'type': 'typing',
            'user': event['user'],
            'is_typing': event['is_typing']
        }))
    
    async def messages_read(self, event):
        """发送消息已读状态到WebSocket"""
        await self.send(text_data=json.dumps({
            'type': 'messages_read',
            'message_ids': event['message_ids'],
            'user_id': event['user_id']
        }))
    
    @database_sync_to_async
    def check_conversation_access(self):
        """检查用户是否有权限访问此会话"""
        try:
            conversation = Conversation.objects.get(id=self.conversation_id)
            return self.scope['user'] in conversation.participants.all()
        except Conversation.DoesNotExist:
            return False
    
    @database_sync_to_async
    def save_message(self, sender, content):
        """保存消息到数据库"""
        conversation = Conversation.objects.get(id=self.conversation_id)
        message = Message.objects.create(
            conversation=conversation,
            sender=sender,
            content=content,
            message_type='text'
        )
        return message
    
    @database_sync_to_async
    def mark_messages_as_read(self, message_ids):
        """标记消息为已读"""
        Message.objects.filter(
            id__in=message_ids,
            conversation_id=self.conversation_id
        ).update(is_read=True)
    
    @database_sync_to_async
    def save_message_to_redis(self, message_data):
        """保存消息到Redis"""
        chat_redis.save_message(self.conversation_id, message_data)
    
    @database_sync_to_async
    def get_other_participant(self):
        """获取会话中的另一个参与者"""
        try:
            conversation = Conversation.objects.get(id=self.conversation_id)
            return conversation.get_other_participant(self.scope['user'])
        except Conversation.DoesNotExist:
            return None


class GroupChatConsumer(AsyncWebsocketConsumer):
    """群组聊天WebSocket消费者"""
    
    async def connect(self):
        self.group_id = self.scope['url_route']['kwargs']['group_id']
        self.group_chat_name = f'group_chat_{self.group_id}'
        
        # 检查用户是否是群成员
        if await self.check_group_membership():
            # 加入群组聊天组
            await self.channel_layer.group_add(
                self.group_chat_name,
                self.channel_name
            )
            
            await self.accept()
        else:
            await self.close()
    
    async def disconnect(self, close_code):
        # 离开群组聊天组
        await self.channel_layer.group_discard(
            self.group_chat_name,
            self.channel_name
        )
    
    async def receive(self, text_data):
        text_data_json = json.loads(text_data)
        message_type = text_data_json.get('type')
        
        if message_type == 'group_message':
            await self.handle_group_message(text_data_json)
        elif message_type == 'typing':
            await self.handle_typing(text_data_json)
    
    async def handle_group_message(self, data):
        """处理群组消息"""
        content = data['message']
        sender = self.scope['user']
        
        # 保存消息到数据库
        message = await self.save_group_message(sender, content)
        
        # 保存消息到Redis
        message_data = {
            'id': message.id,
            'content': message.content,
            'sender_id': sender.id,
            'sender_username': sender.username,
            'sender_nickname': sender.nickname or sender.username,
            'sender_avatar': sender.avatar.url if sender.avatar else None,
            'created_at': message.created_at.isoformat(),
            'message_type': message.message_type,
        }
        await self.save_message_to_redis(message_data)
        
        # 发送消息到群组
        await self.channel_layer.group_send(
            self.group_chat_name,
            {
                'type': 'group_message',
                'message': {
                    'id': message.id,
                    'content': message.content,
                    'sender_id': sender.id,
                    'sender': {
                        'id': sender.id,
                        'username': sender.username,
                        'nickname': sender.nickname or sender.username,
                        'avatar': sender.avatar.url if sender.avatar else None,
                    },
                    'created_at': message.created_at.isoformat(),
                    'message_type': message.message_type,
                }
            }
        )
    
    async def handle_typing(self, data):
        """处理正在输入状态"""
        await self.channel_layer.group_send(
            self.group_chat_name,
            {
                'type': 'typing',
                'user': {
                    'id': self.scope['user'].id,
                    'username': self.scope['user'].username,
                    'nickname': self.scope['user'].nickname or self.scope['user'].username,
                },
                'is_typing': data.get('is_typing', False)
            }
        )
    
    async def group_message(self, event):
        """发送群组消息到WebSocket"""
        await self.send(text_data=json.dumps({
            'type': 'group_message',
            'message': event['message']
        }))
    
    async def typing(self, event):
        """发送正在输入状态到WebSocket"""
        await self.send(text_data=json.dumps({
            'type': 'typing',
            'user': event['user'],
            'is_typing': event['is_typing']
        }))
    
    async def group_update(self, event):
        """发送群组更新通知到WebSocket"""
        await self.send(text_data=json.dumps({
            'type': 'group_update',
            'update': event['update']
        }))
    
    @database_sync_to_async
    def check_group_membership(self):
        """检查用户是否是群成员"""
        try:
            group = Group.objects.get(id=self.group_id)
            return GroupMembership.objects.filter(
                group=group,
                user=self.scope['user']
            ).exists()
        except Group.DoesNotExist:
            return False
    
    @database_sync_to_async
    def save_group_message(self, sender, content):
        """保存群组消息到数据库"""
        group = Group.objects.get(id=self.group_id)
        message = GroupMessage.objects.create(
            group=group,
            sender=sender,
            content=content,
            message_type='text'
        )
        return message
    
    @database_sync_to_async
    def save_message_to_redis(self, message_data):
        """保存消息到Redis"""
        chat_redis.save_message(f'group_{self.group_id}', message_data)


class NotificationConsumer(AsyncWebsocketConsumer):
    """通知WebSocket消费者"""
    
    async def connect(self):
        self.user_id = self.scope['user'].id
        self.notification_group_name = f'notifications_{self.user_id}'
        
        # 加入用户通知组
        await self.channel_layer.group_add(
            self.notification_group_name,
            self.channel_name
        )
        
        await self.accept()
    
    async def disconnect(self, close_code):
        # 离开用户通知组
        await self.channel_layer.group_discard(
            self.notification_group_name,
            self.channel_name
        )
    
    async def receive(self, text_data):
        # 处理客户端发送的数据（如果需要）
        pass
    
    async def send_notification(self, event):
        """发送通知到WebSocket"""
        await self.send(text_data=json.dumps({
            'type': 'notification',
            'notification': event['notification']
        }))
    
    async def send_message_notification(self, event):
        """发送新消息通知"""
        await self.send(text_data=json.dumps({
            'type': 'new_message',
            'message': event['message']
        }))
    
    async def conversation_update(self, event):
        """发送会话更新通知"""
        await self.send(text_data=json.dumps({
            'type': 'conversation_update',
            'conversation': event['conversation']
        }))
